//
//
//  File Name   :  biu.v
//  Used on     :  
//  Author      :  Ted Fried, MicroCore Labs
//  Creation    :  3/13/16
//  Code Type   :  Synthesizable
//
//   Description:
//   ============
//   
//  Bus Interface Unit of the MCL51 processor 
//  ported to the Lattice XO2 Breakout Board.
//
//------------------------------------------------------------------------
//
// Modification History:
// =====================
//
// Revision 1.0 3/13/16 
// Initial revision
//
//
//------------------------------------------------------------------------
//
// Copyright (c) 2020 Ted Fried
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
//------------------------------------------------------------------------
`ifdef __SIM__
`define debug(cmd) cmd 
`else
`define debug(cmd) 
`endif

module biu #(
    parameter   MEM_FILE = "firmware.hex",
    parameter   ROM_SIZE = 4096,
    parameter   RAM_SIZE = 128
)
  (  
    input               CORE_CLK,               // Core Signals
    input               RST_n,

    output     [7:0]           p0 ,     // PO输出口
    input  [7:0]               int_req , //中断请求信号

    output  wire             pxy_wen,
    output  wire [7:0]       pxy_addr,
    output  wire [7:0]       pxy_dout,
    input   wire [7:0]       pxy_din,

    input  [7:0]        EU_BIU_STROBE,          // EU to BIU Signals
    input  [7:0]        EU_BIU_DATAOUT,
    input  [15:0]       EU_REGISTER_R3,
    input  [15:0]       EU_REGISTER_IP,     
    
    
    output [7:0]        BIU_SFR_ACC,            // BIU to EU Signals
    output [15:0]       BIU_SFR_DPTR,
    output [7:0]        BIU_SFR_SP,
    output [7:0]        BIU_SFR_PSW,
    output [7:0]        BIU_RETURN_DATA,
    output              BIU_INTERRUPT,

    output              RESET_OUT
        
  );

//------------------------------------------------------------------------

// Internal Signals

// reg   biu_pxy_rd;
reg   biu_pxy_wr;
reg   core_interrupt_disable;
wire  biu_int2;
wire  biu_int3 = 1'b0;
wire  biu_sfr_select;
wire  acc_parity;

reg  [15:0] eu_register_r3_d1;
reg  [7:0]  biu_sfr_dpl_int;
reg  [7:0]  biu_sfr_dph_int;
reg  [7:0]  biu_sfr_ie_int;
reg  [7:0]  biu_sfr_psw_int;
reg  [7:0]  biu_sfr_acc_int;
reg  [7:0]  biu_sfr_sp_int;
reg  [7:0]  biu_sfr_b_int;
reg  [7:0]  biu_sfr_pxy_addr;
reg  [7:0]  biu_sfr_pxy_dout;
wire [7:0]  biu_sfr_pxy_din;
wire [7:0]  biu_sfr_dataout;
wire [7:0]  biu_sfr_is_int;
wire [7:0]  biu_program_data;
wire [2:0]  eu_biu_strobe_mode;
wire [2:0]  eu_biu_strobe_int;
wire [7:0]  biu_ram_dataout;
wire [7:0]  biu_timer_dataout;  
wire [7:0]  biu_uart_dataout;   
wire [15:0] loader_addr_int;
wire [7:0]  loader_data_int;

//
reg [7:0] biu_sfr_p0_int ;

assign p0 = biu_sfr_p0_int ;

reg [3:0] int_src ;


function integer clog2(input integer d) ;
begin
  for(clog2=0;|d;clog2=clog2+1)
    d = d>>1;
end
endfunction 

localparam ROM_ADDR_HIGH = clog2(ROM_SIZE - 1) - 1;
localparam RAM_ADDR_HIGH = clog2(RAM_SIZE - 1) - 1;
//------------------------------------------------------------------------
//
// User Program ROM.  4Kx8 
//
//------------------------------------------------------------------------                                    

cbb_rom #(
    .MEM_FILE(MEM_FILE),  // ROM初始化文件 
    .MEM_NUM (ROM_SIZE) ,// ROM存储的数量
    .DATA_WIDTH(8) , //数据位宽
    .ENABLE_ASYNC(0)  // 是否异步于时钟输出
) mcl51_ROM (
    .clk(CORE_CLK), 
    .address(EU_REGISTER_IP[ROM_ADDR_HIGH:0]),
    .data_out(biu_program_data)
);

        
//------------------------------------------------------------------------
//
// User Data RAM.  512x8 
//
//------------------------------------------------------------------------                                    
cbb_ram #(
    .DATA_DEPTH    ( RAM_SIZE ) ,  // 数据深度 ， 存的数据个数
    .DATA_WIDTH    ( 8    ),  // 数据位宽
    .ADDR_WIDTH    ( RAM_ADDR_HIGH +1 ),  // 地址位宽
    .ENABLE_ASYNC  ( 0    )   // 是否设置输出异步 1：EDA会使用逻辑单元模拟  0: eda大概率会调用memory
) biu_ram (
    .clk         (CORE_CLK),
    .write_enable(biu_ram_wr),
    .address     (eu_register_r3_d1[RAM_ADDR_HIGH:0]),
    .data_in     (EU_BIU_DATAOUT),
    .data_out    (biu_ram_dataout)
) ;

//------------------------------------------------------------------------
//
// BIU  Combinationals
//
//------------------------------------------------------------------------


// Outputs to the EU
//
assign BIU_SFR_ACC              = biu_sfr_acc_int;
assign BIU_SFR_DPTR             = { biu_sfr_dph_int , biu_sfr_dpl_int };
assign BIU_SFR_SP               = biu_sfr_sp_int;
assign BIU_SFR_PSW              = { biu_sfr_psw_int[7:1] , acc_parity }; 


assign BIU_RETURN_DATA          = (eu_biu_strobe_mode==2'h0) ? biu_program_data     : 
                                  (biu_sfr_select==1'b1)     ? biu_sfr_dataout      : 
                                                               biu_ram_dataout      ;


// Parity for the Accumulator
// This can be removed if parity is not used in firmware.
assign acc_parity = (biu_sfr_acc_int[0]^biu_sfr_acc_int[1]^biu_sfr_acc_int[2]^biu_sfr_acc_int[3]^biu_sfr_acc_int[4]^biu_sfr_acc_int[5]^biu_sfr_acc_int[6]^biu_sfr_acc_int[7]);



                                                               
                                                               
// EU strobes to request BIU processing.
assign eu_biu_strobe_mode[2:0]    = EU_BIU_STROBE[6:4];  
assign eu_biu_strobe_int[2:0]     = EU_BIU_STROBE[2:0]; 



// Select the SFR range if the address is 0x0080 to 0x00FF and addressing mode is Direct
assign biu_sfr_select = ( eu_register_r3_d1[15:7]==9'b0000_0000_1 && eu_biu_strobe_mode[1:0]==3'h1) ? 1'b1 : 1'b0;      


// Decode the write enable to the RAM block
assign biu_ram_wr = (biu_sfr_select==1'b0 && eu_biu_strobe_int==3'h1) ? 1'b1 : 1'b0;


// Mux the SFR data outputs
assign biu_sfr_dataout = (eu_register_r3_d1[7:0]==8'h80) ? biu_sfr_p0_int    :  // P0
                         (eu_register_r3_d1[7:0]==8'h81) ? biu_sfr_sp_int    :
                         (eu_register_r3_d1[7:0]==8'h82) ? biu_sfr_dpl_int   : 
                         (eu_register_r3_d1[7:0]==8'h83) ? biu_sfr_dph_int   : 
                         (eu_register_r3_d1[7:0]==8'hA8) ? biu_sfr_ie_int    : 
                         (eu_register_r3_d1[7:0]==8'hA9) ? biu_sfr_is_int    : 
                         (eu_register_r3_d1[7:0]==8'hC0) ? biu_sfr_pxy_din   : 
                         (eu_register_r3_d1[7:0]==8'hD0) ? biu_sfr_psw_int   : 
                         (eu_register_r3_d1[7:0]==8'hE0) ? biu_sfr_acc_int   : 
                         (eu_register_r3_d1[7:0]==8'hF0) ? biu_sfr_b_int     : 
                                                           8'hEE             ;

                                                          
                                                                   
                                                           
                                                           

                                                        
// Simple fixed priority interrupt controller
// biu_sfr_ie_int[7] is the global_intr_enable 
// biu_sfr_is_int[3:0] contains the interrupt source
// Interrupt 2 = Timer Interrupt        Vector at address 0x4
//           3 = UART-RX Interrupt      Vector at address 0x6
//
// assign BIU_INTERRUPT = (core_interrupt_disable==1'b0 && biu_sfr_ie_int[7]==1'b1 && biu_int2==1'b1) ? 1'b1 : 
//                       //  (core_interrupt_disable==1'b0 && biu_sfr_ie_int[7]==1'b1 && biu_int3==1'b1) ? 1'b1 : 
//                        1'b0;
// //                     (core_interrupt_disable==1'b0 && biu_sfr_ie_int[7]==1'b1 && biu_int4==1'b1) ? 1'b1 :
// //                                                                                                   1'b0 ;
reg [7:0] int_req_latch ;
reg it_oky ;
assign   BIU_INTERRUPT =  (core_interrupt_disable==1'b0 && biu_sfr_ie_int[7]==1'b1 && it_oky) ?  1'b1:1'b0 ;

// 终端入口地址为 biu_sfr_is_int*2 
assign biu_sfr_is_int[7:4] = 4'h1;
assign biu_sfr_is_int[3:0]= int_req_latch[0]?4'h0 :
                            int_req_latch[1]?4'h1 :
                            int_req_latch[2]?4'h2 :
                            int_req_latch[3]?4'h3 :
                            int_req_latch[4]?4'h4 :
                            int_req_latch[5]?4'h5 :
                            int_req_latch[6]?4'h6 :
                            int_req_latch[7]?4'h7 :
                            4'hf ;



reg [7:0] int_req_r ;
wire [7:0] int_req_posedge ;

reg core_interrupt_disable_r;

assign int_req_posedge = (~int_req_r) & int_req  ;

always @(posedge CORE_CLK ) begin

  if( RST_n == 1'b0 ) begin
    int_req_latch <= 8'h00;
    int_req_r <= 8'h00;
    core_interrupt_disable_r<=1'b0 ;
    it_oky = 1'b0;
  end
  else begin
    int_req_r <= int_req ;
    core_interrupt_disable_r <= core_interrupt_disable ;
    
    // if({core_interrupt_disable_r,core_interrupt_disable}==2'b10) begin
    //   int_req_latch <= 8'h00;
    // end

    if({core_interrupt_disable_r,core_interrupt_disable}==2'b01) begin
      it_oky= 1'b0;
    end

    if( int_req_posedge & ~it_oky ) begin 
      int_req_latch <= int_req_posedge;
      it_oky <= 1'b1;
    end
  end
end                    
    
    
//------------------------------------------------------------------------
//
// BIU Controller
//
//------------------------------------------------------------------------
//

always @(posedge CORE_CLK)
begin : BIU_CONTROLLER

  if (RST_n==1'b0)
    begin
      biu_sfr_dpl_int <= 'h0;
      biu_sfr_dph_int <= 'h0;
      biu_sfr_ie_int <= 'h0;
      biu_sfr_psw_int <= 'h0;
      biu_sfr_acc_int <= 'h0;
      biu_sfr_b_int <= 8'h00;
      biu_sfr_sp_int <= 'h7;
      eu_register_r3_d1 <= 'h0;
      // biu_pxy_rd <= 'h0;
      biu_pxy_wr <= 'h0;
      biu_sfr_pxy_addr <= 'h0;
      biu_sfr_pxy_dout <= 'h0;
      core_interrupt_disable <= 'h0;

      biu_sfr_p0_int <= 8'h00;
    end
    
  else    
    begin     
  
      eu_register_r3_d1  <= EU_REGISTER_R3;
      
      if (eu_biu_strobe_int==3'h3)
        begin
          core_interrupt_disable <= 1'b1;
        end
        
      if (eu_biu_strobe_int==3'h4)
        begin
          core_interrupt_disable <= 1'b0;
        end
        
        
         
      // Writes to SFR's
      if (biu_sfr_select==1'b1 && eu_biu_strobe_int==3'h1)
        begin
          case (eu_register_r3_d1[7:0])  // synthesis parallel_case
            8'h80 : begin biu_sfr_p0_int      <= EU_BIU_DATAOUT[7:0]; `debug($display("%.2f us P0 = 0x%h",$realtime/1000.0, EU_BIU_DATAOUT[7:0]) ) ; end 
            8'h81 : biu_sfr_sp_int            <= EU_BIU_DATAOUT[7:0];
            8'h82 : begin biu_sfr_dpl_int     <= EU_BIU_DATAOUT[7:0];end
            8'h83 : begin biu_sfr_dph_int     <= EU_BIU_DATAOUT[7:0];end
            8'hA8 : biu_sfr_ie_int            <= EU_BIU_DATAOUT[7:0];
            8'hD0 : biu_sfr_psw_int           <= EU_BIU_DATAOUT[7:0];
            8'hE0 : biu_sfr_acc_int           <= EU_BIU_DATAOUT[7:0];
            8'hF0 : biu_sfr_b_int             <= EU_BIU_DATAOUT[7:0];
            
            // Proxy Addressing Registers
            8'hC1 : biu_sfr_pxy_dout    <= EU_BIU_DATAOUT[7:0];
            8'hC2 : biu_sfr_pxy_addr    <= EU_BIU_DATAOUT[7:0];
          
            default :  ;
          endcase
        end     
        
        
      // Assert the write strobe to the proxy addressed peripherals
      if (biu_sfr_select==1'b1 && eu_biu_strobe_int==3'h1 && eu_register_r3_d1[7:0]==8'hC1)
        begin
          biu_pxy_wr <= 1'b1;
        end
      else
        begin
          biu_pxy_wr <= 1'b0;
        end     
                    
        
      // Assert the read strobe to the proxy addressed peripherals
      // if (biu_sfr_select==1'b1 && eu_biu_strobe_int==3'h1 && eu_register_r3_d1[7:0]==8'hC2)
      //   begin
      //     biu_pxy_rd <= 1'b1;
      //   end
      // else
      //   begin
      //     biu_pxy_rd <= 1'b0;
      //   end     
        
     end
end



    
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------

//
// Peripherals accessed with proxy addressing  
//
// BIU SFR  biu_sfr_pxy_addr - 0xC2  = Address[7:0]
//          biu_sfr_pxy_dout - 0xC1  = Write Data and strobe to the peripherals
//          biu_sfr_pxy_din  - 0xC0  = Read Data from the peripherals
//
//
//
//------------------------------------------------------------------------
//

// Steer the peripheral read data
// assign biu_sfr_pxy_din = (biu_sfr_pxy_addr[7:4]==4'h0) ? biu_timer_dataout  :
//                          (biu_sfr_pxy_addr[7:4]==4'h1) ? biu_uart_dataout   :
//                                                          8'hEE              ;

// 外设扩展接口
assign pxy_wen = biu_pxy_wr ;
assign pxy_addr = biu_sfr_pxy_addr ;
assign pxy_dout = biu_sfr_pxy_dout ;
assign biu_sfr_pxy_din = pxy_din;
                                      
    
endmodule // biu.v
    
        